'use strict'

/**
 * <--    还原前    -->
let a = 1;
let b = eval('function incr_by_1(x){return x+a} debugger; var p = 9; let foo = 1; const PI = 3.14; incr_by_1(5); p = 100;');
 * 
 * <--    特点    -->
 * 见[eval-code2node-Viife.js -> 对eval内置函数的理解]
 * 
 * <--    解决办法    -->
 * 利用template
 * 
 * <--    冗余判断    -->
 * 见[eval-code2node-Viife.js -> 对eval内置函数的理解]
 * 
 * <--    还原后    -->
var p;
let a = 1;

let b = function () {
  function incr_by_1(x) {
    return x + a;
  }

  debugger;
  p = 9;
  let foo = 1;
  const PI = 3.14;
  incr_by_1(5);
  return p = 100;
}();
 * 
 * <--    比较    -->
 * 跟自己开发的比较来看，大体思路一致，不过由于eval的灵活性，难以确定高下
 * 1. template不能将FunctionDeclaration声明提升；而iife版本也会将函数声明提升。
 * 2. template对var声明的init切断，声明单独提升到外部，在原位置保留为AssignmentExpression；而iife版本直接将var的全部提升。
 * 3. template将声明提升到外层顶部；而iife将声明提升到本Statement前面。
 * 4. template很简单。
 * 
 **/

const {parse} = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const t = require('@babel/types');
const generator = require('@babel/generator').default;
const template = require('@babel/template');

let code = `let a = 1;
let b = eval('function incr_by_1(x){return x+a} debugger; var p = 9; let foo = 1; const PI = 3.14; incr_by_1(5); p = 100;');`

const visitor = {
    CallExpression:{
        enter:function(path){
            if(!t.isIdentifier(path.get('callee').node) || path.get('callee').node.name != 'eval'){return}
            if(path.get('arguments').length != 1 || !t.isStringLiteral(path.get('arguments.0').node)){return}
            let code = path.get('arguments.0').node.value;
            path.replaceWithMultiple(template.statements.ast(code));
        },
    },
}

let ast = parse(code);
traverse(ast, visitor);
console.log(generator(ast).code);
